package org.lixue.game.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.lixue.game.server.config.BaseConfig;
import org.lixue.game.server.handler.socket.MessageDecoder;
import org.lixue.game.server.handler.socket.MessageEncoder;
import org.lixue.game.server.handler.socket.SocketMessageHandler;
import org.lixue.game.server.handler.websocket.WebSocketMessageHandler;

@Slf4j
public class GamerServer {
    private static int MODE_SOCKET = 1;
    private static int MODE_WEB_SOCKET = 2;

    public static void main(String[] args) {
        new NettyServer().start(MODE_SOCKET);
    }

    private static class NettyServer {
        public void start(int mode) {
            log.info("server starting...");
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            if (mode == MODE_WEB_SOCKET) {
                bootstrap.childHandler(new WebSocketChannelInitializer());
            } else {
                bootstrap.childHandler(new NettyChannelInitializer());
            }

            bootstrap.bind(BaseConfig.port);
            log.info("server started on:" + BaseConfig.port);
        }
    }

    private static class NettyChannelInitializer extends ChannelInitializer<SocketChannel> {

        @Override
        protected void initChannel(SocketChannel ch) {
            // in
            ch.pipeline().addLast(new MessageDecoder());
            // out
            ch.pipeline().addLast(new MessageEncoder());

            // idle handler
            ch.pipeline().addLast(
                    new IdleStateHandler(
                            BaseConfig.readerIdleTimeSeconds,
                            BaseConfig.writerIdleTimeSeconds,
                            BaseConfig.allIdleTimeSeconds));

            // business handler
            ch.pipeline().addLast(new SocketMessageHandler());
        }
    }

    private static class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel> {

        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            final ChannelPipeline pipeline = ch.pipeline();

            pipeline.addLast(new HttpServerCodec())
                    .addLast(new HttpObjectAggregator(65536))
                    .addLast(new WebSocketServerCompressionHandler())
                    .addLast(new WebSocketServerProtocolHandler("/", null, true))
                    .addLast(new WebSocketMessageHandler());
        }
    }
}
